home *** CD-ROM | disk | FTP | other *** search
- /* ________________________________________________________
- ________________________________________________________
-
- DTS Groupies - Dave Hersey 9/18/91 MacDTS
-
- This thingy groups a bunch of pictures together and
- takes them apart using PicComments and a QuickDraw
- bottleneck procedure. It's an example of how to
- store and retrieve custom PicComments, among other
- things.
-
- Here's how it works:
-
- All pictures are kept in a global structure for easy
- access. Note that I use the same bounds rectangle
- for all the pictures, but this isn't necessary.
- You should make sure that the enclosing picture is
- wide enough to hold all the others though, or else
- you'll get clipping when they're displayed.
-
- The global structure contains:
-
- • The no. of pictures currently stored.
- • The PicHandles for each of those pictures.
- • The last rectangle each picture was drawn
- in. (For erase and redraw procedures.)
-
- First, the app creates some individual pictures of
- various objects. Then it repeatedly groups and
- ungroups them until the user chooses Quit from the
- File menu.
-
- For demonstration purposes, the app moves the pictures
- around as it alternates between grouped and ungrouped
- stages. This just shows the user that we really have
- separate or consolidated pictures, as the case may be.
-
- Good stuff to look for:
-
- • Example of custom PicComment usage with unique
- identifiers as in Tech Note #181.
- {-> GroupiesPicProc, AssembleGroupies <-}
-
- • Example of a custom QuickDraw bottleneck procedure
- which works in both GrafPorts and CGrafPorts.
- {-> DisassembleGroupies <-}
-
- • Example of finding the deepest device and placing
- a window on it.
- {-> ShowTheGroupies <-}
-
- Lastly, my apologies for all the bad "groupie humor."
-
- {Any similarities to, or resemblance of, actual DTS
- Groupies living or dead is purely coincidental and
- unintentional.}
- ________________________________________________________
- ________________________________________________________ */
-
-
- /*________________Constants___________________*/
-
-
- #define kCreatorType 'EGAD' /* Our creator type. */
-
-
- #define rMenuBar 128 /* The menubar resource ID. */
-
- #define mApple 128 /* Apple menu ID. */
-
- #define iAbout 1 /* "About…" menu item index. */
-
- #define mFile 129 /* File menu ID. */
-
- #define iQuit 1 /* Quit menu item index. */
-
-
- #define kMaxPICTs 50 /* Max. no. of pictures we handle. */
-
- #define kCustomComment 100 /* Custom PicComment indicator. */
-
- #define kSubPICTComment 200 /* Our (sub-picture) sub-PicComment.
- This comment indicates that we've
- stored a picture inside of a
- picture. We use it to extract
- the individual PICTs. */
-
-
- /*__________________Types_____________________*/
-
-
- typedef struct TPICTRec {
- int numPICTs; /* The number of sub-pictures, */
- PicHandle picture[kMaxPICTs]; /* and their PicHandles, */
- Rect curPos[kMaxPICTs]; /* and their last drawn positions. */
- } TPICTRec;
-
-
- /*_________________Globals____________________*/
-
-
- static TPICTRec gPICTRec; /* Our global picture record. */
- static Rect gGroupieBounds; /* The bounds used by our pictures. */
- static Boolean gQuitting; /* "Quitting?" flag. */
- static WindowPtr gTheWindow; /* Our window's pointer. */
-
-
- /*_______________Prototypes___________________*/
-
- extern void AssembleGroupies(void);
- extern pascal void GroupiesPicProc(int kind, int dataSize, Handle dataHandle);
- extern void DisassembleGroupies(void);
- extern void DoMenuCommand(long menuResult);
- extern void GetGroupieEvents(void);
- extern void MoveTheGroupies(Rect *wBounds);
- extern void MakeTheGroupies(void);
- extern void ShowTheGroupies(void);
-
-
-
- /* -------------------------------------
- AssembleGroupies groups all the
- pictures in the global picture record
- into one "composite" picture. It
- removes all the old pictures and
- stores the new one.
- ------------------------------------- */
-
- void AssembleGroupies()
- {
- PicHandle aPICT, groupPICT;
- RgnHandle oldClip;
- int idx;
- long dataSize;
- long ownerApp;
- short localPicComment;
-
- /* Save the old clipping region, and set a valid one so our groupie
- picture develops ok. */
-
- oldClip = NewRgn();
- GetClip(oldClip);
- ClipRect(&gGroupieBounds);
-
- groupPICT = OpenPicture(&gGroupieBounds);
-
-
- /* Create a picture to contain all the other ones, then draw those into
- it, separated by our PicComments. Kill the individual pictures as
- we go. Finally, close the composite picture. */
-
- ownerApp = kCreatorType;
- localPicComment = kSubPICTComment;
-
- for(idx = 0; idx < gPICTRec.numPICTs; idx++)
- {
- aPICT = gPICTRec.picture[idx];
-
-
- /* We don't just use a single custom PicComment since another app may
- use the same comment and conflicts could result. (Not in this app,
- but in the real world.) Instead, we use the method documented in
- Technical Note #181. We add six bytes to the handle and store the
- creator type of the app that made the picture followed by 2 bytes
- for a local PicComment kind within the app. If we used more than
- one PicComment in this app, this extra information would be
- necessary. */
-
- dataSize = GetHandleSize((Handle) aPICT) +6;
- SetHandleSize((Handle) aPICT, dataSize);
-
- BlockMove((Ptr) *aPICT, (Ptr) *aPICT +6, dataSize -6);
- BlockMove(&ownerApp, (Ptr) *aPICT, 4);
- BlockMove(&localPicComment, (Ptr) *aPICT +4, 2);
-
- PicComment(kCustomComment, dataSize, (Handle) aPICT);
-
- /* Fix the original PicHandle so that we can draw our picture for apps
- that don't know about our custom comments. */
-
- BlockMove((Ptr) *aPICT +6, (Ptr) *aPICT, dataSize -6);
- SetHandleSize((Handle) aPICT, dataSize -6);
- DrawPicture(aPICT, &(*aPICT)->picFrame);
- KillPicture(aPICT);
- gPICTRec.picture[idx] = NULL;
- }
-
- ClosePicture();
-
-
- /* Restore the original clipping region and update our global picture
- record so that we have one consolidated picture, in the first slot.
- We set it's current position to (0, 0, 0, 0) so that we don't waste
- time erasing anything on the first draw. */
-
- SetClip(oldClip);
- DisposeRgn(oldClip);
-
- gPICTRec.numPICTs = 1;
- gPICTRec.picture[0] = groupPICT;
- SetRect(&gPICTRec.curPos[0], 0, 0, 0, 0);
- }
-
-
- /* -------------------------------------
- GroupiesPicProc is our replacement
- for the port's StdCommentProc.
- ------------------------------------- */
-
- pascal void GroupiesPicProc(int kind, int dataSize, Handle dataHandle)
- {
- int nextNum;
- long ownerApp;
- short localPicComment;
- Handle theHandle;
-
- /* If this is a custom PicComment, see if it's ours. In this app,
- we know it always will be, but when you import other pictures
- you can't be so sure. */
-
- if (kind == kCustomComment && (gPICTRec.numPICTs < kMaxPICTs))
- {
- if (dataSize < 6) return; /* Not ours? */
-
- BlockMove((Ptr) *dataHandle, &ownerApp, 4);
- BlockMove((Ptr) *dataHandle +4, &localPicComment, 2);
-
- if ((ownerApp != kCreatorType) || /* Not ours? */
- (localPicComment != kSubPICTComment)) return;
-
-
- /* This is indeed our picture comment. Create a handle for the data we
- found, store it in our global picture record and bump the number of
- pictures we have. The reason that we clear the picture's curPos
- rect is so that we won't waste time erasing anything the first time
- we enter MoveTheGroupies. */
-
- nextNum = gPICTRec.numPICTs;
- gPICTRec.picture[nextNum] = (PicHandle) dataHandle;
- SetRect(&gPICTRec.curPos[nextNum], 0, 0, 0, 0);
-
-
- /* After we create the handle for the data, we have to remember that
- we have 6 bytes of identifying "garbage" in front of the picture
- data. To remove that, BlockMove all the picture data to the
- beginning of the handle and reset the handle's size. This is kind
- of a hassle, but it's really best to store your custom PicComments
- this way. Otherwise, you may misinterpret someone elses comments
- or cause them to misinterpret yours. */
-
- if (HandToHand((Handle *) &gPICTRec.picture[nextNum]) == noErr)
- {
- ++gPICTRec.numPICTs;
- theHandle = (Handle) gPICTRec.picture[nextNum];
- BlockMove((Ptr) *theHandle +6, (Ptr) *theHandle, dataSize -6);
- SetHandleSize(theHandle, dataSize -6);
- }
- }
- }
-
-
- /* -------------------------------------
- DisassembleGroupies ungroups the
- first picture in the global picture
- record. It replaces that picture
- with new pictures of every picture
- it contained. All drawing is done
- within another "dummy" picture
- so that nothing draws on the screen.
- The reason we can't use an empty
- clipping region to do this is that
- PicComments will be clipped out along
- with everything else, and we'd be
- hosed. (We need the PicComments!)
-
- This code is written so that it
- installs the GrafProcs correctly for
- both GrafPorts and CGrafPorts.
- ------------------------------------- */
-
- void DisassembleGroupies()
- {
- GrafPtr curPort;
- QDProcs theQDProcs; /* If we're using a GrafPort… */
- CQDProcs theCQDProcs; /* If we're using a CGrafPort… */
- PicHandle dummyPICT;
-
- /* Reset the number of pictures in our global picture record to zero.
- There's actually one picture there at this point (the composite
- one), but we must set this to zero so that our PicComment handler
- stores extracted pictures in the right place. */
-
- gPICTRec.numPICTs = 0;
-
-
- /* Get the current port and the standard QDProcs or CQDProcs,
- depending on whether we have a GrafPort or CGrafPort. */
-
- GetPort(&curPort);
-
- if (curPort->portBits.rowBytes < 0) /* CGrafPort… */
- {
- SetStdCProcs(&theCQDProcs);
- theCQDProcs.commentProc = (Ptr) &GroupiesPicProc;
- curPort->grafProcs = (QDProcsPtr) &theCQDProcs;
- }
- else /* GrafPort… */
- {
- SetStdProcs(&theQDProcs);
- theQDProcs.commentProc = (Ptr) &GroupiesPicProc;
- curPort->grafProcs = (QDProcsPtr) &theQDProcs;
- }
-
-
- /* Open our dummy picture and draw into it so that our PicComment
- handler is called to parse the picture. When finished, close the
- picture, kill it and remove our grafProcs. */
-
- dummyPICT = OpenPicture(&(*gPICTRec.picture[0])->picFrame);
- DrawPicture(gPICTRec.picture[0], &(*gPICTRec.picture[0])->picFrame);
- ClosePicture();
- KillPicture(dummyPICT);
-
- curPort->grafProcs = NULL;
- }
-
-
- /* -------------------------------------
- DoMenuCommand handles our menu items.
- ------------------------------------- */
-
- void DoMenuCommand(long menuResult)
- {
- int menuID, menuItem;
- Str255 daName;
- MenuHandle theMenu;
- GrafPtr savePort;
-
- /* Get the menu ID and item ID. */
-
- menuID = (menuResult >>16) & 0xFFFF;
- menuItem = menuResult & 0xFFFF;
-
-
- /* Do what we're supposed to. */
-
- switch (menuID)
- {
- case mApple: /* Apple Menu */
- switch (menuItem)
- {
- case iAbout: /* -> Handle "About…" */
- break;
-
- default: /* -> The rest are DAs. */
-
- GetPort(&savePort);
- GetItem(GetMHandle(mApple), menuItem, (ConstStr255Param) daName);
- OpenDeskAcc(daName);
- SetPort(savePort);
- break;
- };
-
- case mFile: /* File Menu */
- switch (menuItem)
- {
- case iQuit:
- gQuitting = true; /* -> Quit */
- break;
- }
-
- };
-
- HiliteMenu(0);
-
- };
-
-
- /* -------------------------------------
- GetGroupieEvents is a main event
- loop. It calls WaitNextEvent and
- other nice stuff. It also makes our
- Groupies assemble, disassemble, and
- hop about.
- ------------------------------------- */
-
- void GetGroupieEvents()
- {
- EventRecord theEvent;
- WindowPtr whichWindow;
- short partCode, idx;
- Rect dragRect;
- RgnHandle grayRgn;
- char key, time;
- long finalTicks;
-
-
- /* Set up the rectangle for where we can drag windows. Initialize
- our "time-through-the-loop" counter to -1 so that it gets bumped
- to zero on the first pass. This will enable us assemble the
- grouped picture as we go through the first time. */
-
- grayRgn = GetGrayRgn();
- dragRect = (*grayRgn)->rgnBBox;
- time = -1;
-
-
- /* We have a counter which goes from 0-26 and is incremented each time
- we go through this code. At time = 0, We assemble the grouped image.
- At time = 12, we break all the PICTs out of it. At time = 27, we
- cycle back to time = 0. In between these life altering times,
- (at least for groupies), we draw all of our current pictures in
- random places. This clearly shows whether the PICTs are currently
- grouped or not. We go through this loop until the user quits. */
-
- do
- {
- SetPort(gTheWindow);
- time = ++time % 27;
-
- if (time == 0)
- {
- AssembleGroupies(); /* Group the pictures. */
- EraseRect(&(gTheWindow)->portRect);
- }
-
- if (time == 12)
- {
- DisassembleGroupies(); /* Ungroup the pictures. */
- EraseRect(&(gTheWindow)->portRect);
- }
-
-
- /* Move all pictures so we can see their current state. */
-
- MoveTheGroupies(&(gTheWindow)->portRect);
-
-
- /* Delay so our graphics don't flash. */
-
- Delay((time < 12)? 40:10, &finalTicks);
-
-
- /* Handle any pending events. */
-
- if (WaitNextEvent(everyEvent, &theEvent, 0, NULL))
- switch (theEvent.what)
- {
- case mouseDown: /* Handle mouse clicks. */
-
- partCode = FindWindow(theEvent.where, &whichWindow);
-
- switch (partCode)
- {
- case inContent:
- if (whichWindow != FrontWindow())
- SelectWindow(whichWindow);
- break;
-
- case inDrag:
- DragWindow(whichWindow, theEvent.where, &dragRect);
- break;
-
- case inMenuBar:
- DoMenuCommand(MenuSelect(theEvent.where));
-
- case inSysWindow:
- SystemClick(&theEvent, whichWindow);
- break;
- }
- break;
-
- case updateEvt: /* Handle update events. */
- BeginUpdate((WindowPtr) theEvent.message);
- EndUpdate((WindowPtr) theEvent.message);
- break;
-
- case keyDown: /* Handle key presses. */
- case autoKey:
- key = (char) (theEvent.message & charCodeMask);
- if (((theEvent.modifiers & cmdKey) != 0) && (theEvent.what == keyDown))
- DoMenuCommand(MenuKey(key));
- break;
- }
- }
- while (!gQuitting);
- }
-
-
- /* -------------------------------------
- MoveTheGroupies moves the current
- pictures… somewhere randomly. It
- first erases all the pictures in
- descending order. Then it redraws
- them in new locations in ascending
- order. This way we don't wipe out
- any of the new pictures when the old
- ones are erased.
- ------------------------------------- */
-
- void MoveTheGroupies(Rect *wBounds)
- {
- int newLeft, newTop, width, height, idx;
- float maxX, maxY;
- Rect picFrame, curPos;
-
- /* First erase all pictures in reverse order. Also, calculate their
- new locations and store those in their curPos fields. */
-
- for (idx = gPICTRec.numPICTs -1; idx >= 0; idx--)
- {
- curPos = gPICTRec.curPos[idx];
- EraseRect(&curPos);
-
- picFrame = (*gPICTRec.picture[idx])->picFrame;
- width = picFrame.right -picFrame.left;
- height = picFrame.bottom -picFrame.top;
-
-
- /* To calculate new positions, we find the maximum position we can
- have for the picture's top left corner. Then, we find a random
- point that's bounded by (0, 0) and that maximum. Finally, we
- set this picture's current position so that it has this point for
- its top left corner. */
-
- maxX = (wBounds->right - wBounds->left) -width;
- maxY = (wBounds->bottom - wBounds->top) -height;
-
- newTop = (((float) Random() +32767)/65534.0) * maxX;
- newLeft = (((float) Random() +32767)/65534.0) * maxY;
-
- curPos.top = newTop;
- curPos.left = newLeft;
- curPos.bottom = newTop +height;
- curPos.right = newLeft +width;
- gPICTRec.curPos[idx] = curPos;
- }
-
-
- /* Now draw all the pictures in their new positions. */
-
- for (idx = 0; idx < gPICTRec.numPICTs; idx++)
- DrawPicture(gPICTRec.picture[idx], &gPICTRec.curPos[idx]);
- }
-
-
- /* -------------------------------------
- MakeTheGroupies creates the pictures
- that will be grouped. These can be
- any QuickDraw pictures. For this
- example, I use four pictures; one
- containing a square, one with a
- circle, one with a triangle and one
- with some text. These are all stored
- in the global picture record.
-
- This routine is only called once, to
- put some pictures into the works to
- start with.
- ------------------------------------- */
-
- void MakeTheGroupies()
- {
- RgnHandle oldClip;
- PolyHandle trianglePoly;
- int fNum, vPos;
-
- /* Save the current clipping region so that we can restore it later.
- Set our own clipping region, so that we know we have a valid one.
- Also initialize the number of pictures in our global picture
- structure to zero. */
-
- oldClip = NewRgn();
- GetClip(oldClip);
-
- SetRect(&gGroupieBounds, 0, 0, 150, 150);
- ClipRect(&gGroupieBounds);
-
- gPICTRec.numPICTs = 0;
-
-
- /* Create a picture with a blue square in it. We set the curPos
- rectangle for all of these pictures to (0, 0, 0, 0) so that
- we don't do any unnecessary erasing the first time they enter
- MoveTheGroupies. */
-
- gPICTRec.picture[0] = OpenPicture(&gGroupieBounds);
- ForeColor(blueColor);
- PaintRect(&gGroupieBounds);
- ClosePicture();
- SetRect(&gPICTRec.curPos[0], 0, 0, 0, 0);
- ++gPICTRec.numPICTs;
-
-
- /* Create a picture with a red circle in it. */
-
- gPICTRec.picture[1] = OpenPicture(&gGroupieBounds);
- ForeColor(redColor);
- PaintOval(&gGroupieBounds);
- ClosePicture();
- SetRect(&gPICTRec.curPos[1], 0, 0, 0, 0);
- ++gPICTRec.numPICTs;
-
-
- /* Create a picture with a green triangle in it. */
-
- gPICTRec.picture[2] = OpenPicture(&gGroupieBounds);
- ForeColor(greenColor);
-
- trianglePoly = OpenPoly();
- MoveTo(gGroupieBounds.left, gGroupieBounds.bottom);
- LineTo((gGroupieBounds.right - gGroupieBounds.left)/2, gGroupieBounds.top);
- LineTo(gGroupieBounds.right, gGroupieBounds.bottom);
- LineTo(gGroupieBounds.left, gGroupieBounds.bottom);
- ClosePoly();
-
- PaintPoly(trianglePoly);
- KillPoly(trianglePoly);
- ClosePicture();
- SetRect(&gPICTRec.curPos[2], 0, 0, 0, 0);
- ++gPICTRec.numPICTs;
-
-
- /* Create a picture with some text in it. */
-
- gPICTRec.picture[3] = OpenPicture(&gGroupieBounds);
- ForeColor(blackColor);
-
- GetFNum((ConstStr255Param) "\pTimes", &fNum);
- TextFont(fNum);
- TextSize(12);
- TextFont(bold);
- vPos = gGroupieBounds.top +(gGroupieBounds.bottom - gGroupieBounds.top)/2;
- MoveTo(gGroupieBounds.left +10, vPos +10);
- DrawString((ConstStr255Param) "\pWe are the Groupies!");
- ClosePicture();
- SetRect(&gPICTRec.curPos[3], 0, 0, 0, 0);
- ++gPICTRec.numPICTs;
-
-
- /* Restore the original clipping region. */
-
- SetClip(oldClip);
- DisposeRgn(oldClip);
- }
-
-
- /* -------------------------------------
- ShowTheGroupies starts the show.
- First, we find the deepest display
- because the groupies are a colorful
- bunch. Then we create a window and
- some Groupies. Finally, we jump into
- our main event loop.
- ------------------------------------- */
-
- void ShowTheGroupies()
- {
- Rect maxRect, deepRect, wBounds;
- GDHandle deepGDH;
-
- /* Find the bounds of the deepest device. We'll use this to determine
- where to put our window. Passing the maximum enclosing rectangle
- to GetMaxDevice assures that we find the deepest device available. */
-
- SetRect(&maxRect, -32767, -32767, 32767, 32767);
- deepGDH = GetMaxDevice(&maxRect);
- deepRect = (*deepGDH)->gdRect;
-
-
- /* Create a window for our drawing, offset onto the deepest device. */
-
- SetRect(&wBounds, 40, 40, 360, 340);
- OffsetRect(&wBounds, wBounds.left +deepRect.left, wBounds.top +deepRect.top);
-
- gTheWindow = NewWindow(nil, &wBounds, (ConstStr255Param) "\pDTS Groupies!", true, noGrowDocProc, (WindowPtr) -1, false, 1234);
- SetPort(gTheWindow);
-
-
- /* Create our pictures to group, then go into the work loop. This
- loop continually groups the pictures, draws the grouped picture
- in different locations, ungroups the picture, draws the ungrouped
- pictures in different locations and repeats until the user quits. */
-
- MakeTheGroupies();
- GetGroupieEvents();
- }
-
-
- /* -------------------------------------
- main.
- ------------------------------------- */
-
- main()
- {
- Handle menuBar;
-
- /* Initialize the toolbox routines. */
-
- InitGraf(&thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- InitDialogs(nil);
- InitCursor();
-
-
- /* Set up our menubar. */
-
- menuBar = GetNewMBar(rMenuBar); /* Read menus into menu bar */
- SetMenuBar(menuBar); /* and install them. */
- DisposHandle(menuBar);
-
- AddResMenu(GetMHandle(mApple), 'DRVR');
- DrawMenuBar();
-
-
- /* Initialize the random number seed for our hopping groupies and set
- our quitting flag to false. Call the routine that runs everything,
- then, quit. */
-
- GetDateTime((unsigned long *) &randSeed);
- gQuitting = false;
- ShowTheGroupies();
- }
-